package com.xayy.ha.gen.lite.util;

import cn.hutool.core.collection.CollectionUtil;
import com.xayy.ha.gen.lite.config.GenerateConfig;
import com.xayy.ha.gen.lite.config.JavaTypeMapper;
import com.xayy.ha.gen.lite.entity.GenTabField;
import com.xayy.ha.gen.lite.entity.GenTabInfo;
import com.xayy.ha.gen.lite.mapper.DmDbTableMapper;
import com.xayy.ha.gen.lite.mapper.MysqlDbTableMapper;
import com.xayy.ha.gen.lite.mapper.PgDbTableMapper;
import com.xayy.ha.gen.lite.mapper.SqliteTableMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhoutanxin@gmail.com
 * @date 2022/7/30 23:46
 */
@Component
public class GeneratorUtil {
    @Autowired
    GenerateConfig generateConfig;
    @Autowired
    MysqlDbTableMapper mysqlDbTableMapper;
    @Autowired
    PgDbTableMapper pgDbTableMapper;
    @Autowired
    SqliteTableMapper sqliteTableMapper;
    @Autowired
    DmDbTableMapper dmDbTableMapper;
    @Value("${spring.datasource.dynamic.primary}")
    private String primary;
    public  void create(){
        System.out.println("生成代码start......");

        //获取页面或者配置文件的参数
        Map<String, Object> templateData = new HashMap<String, Object>();
        templateData.put("tableName", generateConfig.tableName);
        System.out.println("表名=="+ generateConfig.tableName);
        templateData.put("toCamelCase", generateConfig.toCamelCase);
        System.out.println("是否驼峰命名=="+ generateConfig.toCamelCase);
        templateData.put("entityName", generateConfig.entityName);
        System.out.println("实体类名称=="+ generateConfig.entityName);

        templateData.put("packageNamePre", generateConfig.packageNamePre);
        System.out.println("包名前缀=="+ generateConfig.packageNamePre);

        templateData.put("templatePath", generateConfig.getTemplatePath());
        System.out.println("模板路径=="+ generateConfig.getTemplatePath());

        templateData.put("excludeField", generateConfig.getExcludeField());
        System.out.println("排除字段=="+ generateConfig.getExcludeField());


        //支持自定义输出路径
        if(StringUtils.isNotBlank(generateConfig.outUrl)){
            templateData.put("outUrl", generateConfig.outUrl);
        } else {
            String path = GeneratorUtil.class.getClassLoader().getResource("").getPath() + "../../src/main/java";
            templateData.put("outUrl", path);
        }
        System.out.println("生成文件路径为=="+ templateData.get("outUrl"));

        templateData.put("authorName", generateConfig.authorName);
        System.out.println("以后代码出问题找=="+ generateConfig.authorName);


        templateData.put("databaseName", generateConfig.databaseName);
        templateData.put("ipName", generateConfig.ipName);
        templateData.put("portName", generateConfig.portName);
        templateData.put("userName", generateConfig.userName);
        templateData.put("passWord", generateConfig.passWord);

        //主键ID
        templateData.put("primaryId", generateConfig.primaryId);

        //模块名称
        templateData.put("moduleName", generateConfig.moduleName);
        try {
            //生成代码文件
            generate(templateData);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("生成代码end......");

    }


    public void generate(Map<String, Object> templateData) {
        //包前缀
        String packagePreAndModuleName = getPackagePreAndModuleName(templateData);

        templateData.put("controllerPackageName", String.format(packagePreAndModuleName + ".controller",
                templateData.get("entityName").toString().toLowerCase()));
        templateData.put("dtoPackageName", String.format(packagePreAndModuleName + ".model.dto",
                templateData.get("entityName").toString().toLowerCase()));
        templateData.put("voPackageName", String.format(packagePreAndModuleName + ".model.vo",
                templateData.get("entityName").toString().toLowerCase()));
        //支持对应实体插入在前面，需要带上%s
        templateData.put("entityPackageName", String.format(packagePreAndModuleName + ".model",
                templateData.get("entityName").toString().toLowerCase()));
        templateData.put("mapperPackageName", String.format(packagePreAndModuleName + ".mapper",
                templateData.get("entityName").toString().toLowerCase()));
        templateData.put("servicePackageName", String.format(packagePreAndModuleName + ".service",
                templateData.get("entityName").toString().toLowerCase()));
        templateData.put("serviceImplPackageName", String.format(packagePreAndModuleName + ".service.impl",
                templateData.get("entityName").toString().toLowerCase()));

        templateData.put("convertPackageName", String.format(packagePreAndModuleName + ".convert",
                templateData.get("entityName").toString().toLowerCase()));

        templateData.put("currentTime", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));

        //======================生成文件配置======================
        try {
            // 生成Entity
            String entityName = String.format("%s", templateData.get("entityName").toString());
            generateFile("entity.java.ftl", templateData, templateData.get("entityPackageName").toString(), entityName+".java");
            //如果是ds-server服务调用底层接口，不需要生成vo、dto、dao、convert
            if(generateConfig.getTemplatePath().startsWith("ds")) {
                // 生成dto
                String dtoName = String.format("%sDto", templateData.get("entityName").toString());
                templateData.put("dtoName", dtoName);
                generateFile("dto.java.ftl", templateData, templateData.get("dtoPackageName").toString(),
                        dtoName + ".java");

                String paramName = String.format("%sVo", templateData.get("entityName").toString());
                templateData.put("voName", paramName);
                generateFile("vo.java.ftl", templateData, templateData.get("voPackageName").toString(),
                        paramName + ".java");

                // 生成DAO
                String daoName = String.format("%sMapper", templateData.get("entityName").toString());
                templateData.put("mapperName", daoName);
                generateFile("mapper.java.ftl", templateData, templateData.get("mapperPackageName").toString(),
                        daoName + ".java");

                // 生成Convert
                String convertName = String.format("%sConvert", templateData.get("entityName").toString());
                templateData.put("convertName", convertName);
                generateFile("convert.java.ftl", templateData, templateData.get("convertPackageName").toString(),
                        convertName + ".java");
            }
            // 生成Service
            String serviceName = String.format("%sService", templateData.get("entityName").toString());
            templateData.put("serviceName", serviceName);
            generateFile("service.java.ftl", templateData, templateData.get("servicePackageName").toString(),
                    serviceName + ".java");



            // 生成ServiceImpl
            String serviceImplName = String.format("%sServiceImpl", templateData.get("entityName").toString());
            templateData.put("serviceImplName", serviceImplName);
            generateFile("serviceImpl.java.ftl", templateData, templateData.get("serviceImplPackageName").toString(),
                    serviceImplName + ".java");



            // 生成Controller
            String controllerName = String.format("%sController", templateData.get("entityName").toString());
            templateData.put("controllerName", controllerName);
            generateFile("controller.java.ftl", templateData, templateData.get("controllerPackageName").toString(),
                    controllerName + ".java");


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成文件
     * @param templateName 模板名称
     * @param templateData 参数名
     * @param packageName 包名
     * @param fileName 文件名
     */
    public void generateFile(String templateName, Map<String, Object> templateData, String packageName, String fileName) {
        templateData.put("fileName", fileName);

        // 获取数据库参数
        if("entity.java.ftl".equals(templateName)
                || "dto.java.ftl".equals(templateName)|| "vo.java.ftl".equals(templateName)){
            List<GenTabField> propList = new ArrayList<>();
            GenTabInfo genTabInfo =null;
            if ("postgres".equals(primary)){
                propList = pgDbTableMapper.selectTableColumn(generateConfig.tableName);
                GenTabField genTabField =propList.size()>0?propList.get(0):null;
                genTabInfo = GenTabInfo.builder()
                        .tableName(genTabField.getTableName())
                        .tableComment(StringUtils.isNotBlank(genTabField.getTableComment())? genTabField.getTableComment():"")
                        .build();
            }else if("dm".equals(primary)){
                propList = dmDbTableMapper.selectTableColumn(generateConfig.tableName);
                genTabInfo = dmDbTableMapper.selectTableInfo(generateConfig.tableName);
            }else if("mysql".equals(primary)){
                propList = mysqlDbTableMapper.selectTableColumn(generateConfig.tableName);
                genTabInfo = mysqlDbTableMapper.selectTableInfo(generateConfig.tableName);
            }else if("sqlite".equals(primary)){
                genTabInfo = sqliteTableMapper.selectTableInfo(generateConfig.tableName);
                List<Map<String,Object>> tmpPropList = sqliteTableMapper.selectTableColumn(generateConfig.tableName);
                GenTabField genTabField =null;
                for(Map<String,Object> tmpCol:tmpPropList){
                    String dataType=(String)tmpCol.get("type");
                    if(dataType.contains("(")){
                        dataType=dataType.substring(0,dataType.indexOf("("));
                    }
                    genTabField = GenTabField.builder()
                            .tableName(genTabInfo.getTableName())
                            .tableComment(genTabInfo.getTableComment())
                            .columnName((String)tmpCol.get("name"))
                            .columnType(dataType)
                            .dataType(dataType)
                            .build();
                    propList.add(genTabField);
                }
            }
            String excludeField=(String)templateData.get("excludeField");
            List excludeFiledList=Arrays.asList(excludeField.split(","));
            List<GenTabField> afterExcludeProps=new ArrayList<>();
            List<String>  importPackageList=new LinkedList<>();
            propList.stream().forEach(tableField -> {
                JavaTypeMapper fieldType = colType2JavaType(tableField.getDataType());
                if (fieldType.isImportPackage()){
                    if(!CollectionUtil.contains(importPackageList,fieldType.getPackageName())){
                        importPackageList.add(fieldType.getPackageName());
                    }

                }
                tableField.setJavaType(fieldType.getJavaType());
                boolean toCamelCase=(boolean)templateData.get("toCamelCase");
                if(toCamelCase){
                    tableField.setCaseAttrName(convertToCamelCase(tableField.getColumnName()));
                }else{
                    tableField.setCaseAttrName(tableField.getColumnName());
                }
                if(excludeFiledList.size()>0){
                    if(!excludeFiledList.contains(tableField.getColumnName())){
                        afterExcludeProps.add(tableField);
                    }
                }
            });
            templateData.put("pros",propList);
            if(genTabInfo !=null)templateData.put("tabInfo", genTabInfo);
            templateData.put("afterExcludeProps",afterExcludeProps);
            templateData.put("importPackages",importPackageList);
        }
        try {
            // 默认生成文件的路径
            FreeMakerUtil freeMakerUtil = new FreeMakerUtil();
            freeMakerUtil.generateFile(templateName, templateData, packageName, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 封装包名前缀
     * @return
     */
    private String getPackagePreAndModuleName(Map<String, Object> templateData){
        String packageNamePre = templateData.get("packageNamePre").toString();
        String moduleName = templateData.get("moduleName").toString();
        if(StringUtils.isNotBlank(moduleName)){
            return packageNamePre + "." + moduleName;
        }
        return packageNamePre;
    }


    /**
     * 数据库类型转Java类型
     *
     * @param type
     * @return
     */
    public  JavaTypeMapper colType2JavaType(String type) {
        List<JavaTypeMapper> typeMapperConfig=generateConfig.getTypeMapperConfig();
        JavaTypeMapper defaultType = new JavaTypeMapper();
        defaultType.setJavaType("Object");
        if (StringUtils.isNotBlank(type) && CollectionUtil.isNotEmpty(typeMapperConfig)) {
            JavaTypeMapper typeConfig = typeMapperConfig.stream().filter(item -> item.getColumnType().equalsIgnoreCase(type)).findFirst().orElse(defaultType);
            return typeConfig;
        } else {
            return defaultType;
        }
    }

    /**
     * 将带下滑线的英文字段转换为驼峰式命名
     * @return
     * 返回值类型: String
     */
    private  Pattern underLinePattern = Pattern.compile("_(\\w)");
    public  String convertToCamelCase(String str) {
        str = str.toLowerCase();
        Matcher matcher = underLinePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()){
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
